#include "stdafx.h"
#include "Renderer.h"

#include "../Effects11/Inc/d3dx11effect.h"

#include "GlobalAppData.h"
#include "InputLayouts.h"

using namespace mgfx;

Renderer * Renderer::Instance = NULL;

namespace mgfx
{
	struct Texture
	{
		ID3D11Resource * Res;
		ID3D11RenderTargetView * RTV;
		ID3D11DepthStencilView * DSV;
		ID3D11ShaderResourceView * SRV;
		ID3D11UnorderedAccessView * UAV;
		uint Width, Height, Depth;
		DXGI_FORMAT Format;

		Texture() { SecureZeroMemory(this, sizeof(Texture)); }
	};

	struct Shader
	{
		ID3DX11Effect * Effect;
		ID3DX11EffectPass * Pass;
	};

	struct VertexBuffer
	{
		ID3D11Buffer * Buffer;
		uint NumVertices;
		size_t VertexSize;
	};

	struct IndexBuffer
	{
		ID3D11Buffer * Buffer;
		uint NumIndices;
		size_t IndexSize;
	};

	struct VertexFormat
	{
		ID3D11InputLayout * InputLayout;
	};

}

static void releaseTexture(Texture & texture)
{
	ReleaseCOM(texture.RTV);
	ReleaseCOM(texture.DSV);
	ReleaseCOM(texture.SRV);
	ReleaseCOM(texture.UAV);
	ReleaseCOM(texture.Res);
}

static D3D11_VIEWPORT copyViewport(const Viewport & vp)
{
	D3D11_VIEWPORT d3dVp;
	d3dVp.TopLeftX = vp.TopLeftX; d3dVp.TopLeftY = vp.TopLeftY;
	d3dVp.MinDepth = vp.MinDepth; d3dVp.MaxDepth = vp.MaxDepth;
	d3dVp.Width = vp.Width;		  d3dVp.Height = vp.Height;
	return d3dVp;
}

static Viewport copyViewport(const D3D11_VIEWPORT & d3dVp)
{
	Viewport vp;
	vp.TopLeftX = d3dVp.TopLeftX; vp.TopLeftY = d3dVp.TopLeftY;
	vp.MinDepth = d3dVp.MinDepth; vp.MaxDepth = d3dVp.MaxDepth;
	vp.Width = d3dVp.Width;		vp.Height = d3dVp.Height;
	return vp;
}

static const D3D11_PRIMITIVE_TOPOLOGY primTopologies[] = {
	D3D10_PRIMITIVE_TOPOLOGY_UNDEFINED,
	D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST
};

Renderer::Renderer(ID3D11Device * device, ID3D11DeviceContext * context,
	ID3D11RenderTargetView * backbuffer, ID3D11DepthStencilView * depthStencil)
{
	mDevice = device;
	mContext = context;

	// store backbuffer as texture
	Texture defaultFramebuffer;
	defaultFramebuffer.RTV = backbuffer;
	mTextures.push_back(defaultFramebuffer);

	memset(mCurrentColorRTs, TEXTURE_NONE, sizeof(mCurrentColorRTs));
	mDefaultFramebuffer = mCurrentColorRTs[0] = 0;

	// store default depth-stencil as texture
	Texture defaultDepthStencil;
	defaultDepthStencil.DSV = depthStencil;
	mTextures.push_back(defaultDepthStencil);

	mDefaultDepthStencil = mCurrentDepthRt = 1;

	mCurrentPrimType = PRIMITIVE_NONE;
	mCurrentVertexBuffer = VB_NONE;
	mCurrentIndexBuffer = IB_NONE;
	mCurrentShader = SHADER_NONE;
}

void Renderer::init()
{
	createFullScreenQuadBuffers();
}

Renderer::~Renderer()
{
	for(size_t i = 2; i < mTextures.size(); i++)
		releaseTexture(mTextures[i]);

	for(size_t i = 0; i < mShaders.size(); i++)
		ReleaseCOM(mShaders[i].Effect);

	for(size_t i = 0; i < mVertexFormats.size(); i++)
		ReleaseCOM(mVertexFormats[i].InputLayout);

	for(size_t i = 0; i < mVertexBuffers.size(); i++)
		ReleaseCOM(mVertexBuffers[i].Buffer);

	for(size_t i = 0; i < mIndexBuffers.size(); i++)
		ReleaseCOM(mIndexBuffers[i].Buffer);
}

void Renderer::setShader(ShaderID shader)
{
	mCurrentShader = shader;
}

void Renderer::setShaderResource(const char * name, TextureID texId)
{
	mShaders[mCurrentShader].Effect->GetVariableByName(name)->AsShaderResource()->SetResource(
		texId == TEXTURE_NONE ? NULL : mTextures[texId].SRV);
}

void Renderer::setShaderResourceArray(const char * name, TextureID * texIds, uint count)
{
	ID3D11ShaderResourceView * SRVs[MAX_SHADER_RESOURCE_ARRAY_SIZE];
	for(uint i = 0; i < count; i++)
	{
		SRVs[i] = texIds[i] == TEXTURE_NONE ? NULL : mTextures[texIds[i]].SRV;
	}
	mShaders[mCurrentShader].Effect->GetVariableByName(name)->AsShaderResource()->SetResourceArray(SRVs, 0, count);
}

void Renderer::setShaderUAV(const char * name, TextureID texId)
{
	mShaders[mCurrentShader].Effect->GetVariableByName(name)->AsUnorderedAccessView()->SetUnorderedAccessView(
		texId == TEXTURE_NONE ? NULL : mTextures[texId].UAV);
}

void Renderer::setShaderMatrix(const char * name, const float * data)
{
	mShaders[mCurrentShader].Effect->GetVariableByName(name)->AsMatrix()->SetMatrix(data);
}

void Renderer::setShaderMatrixArray(const char * name, const float * data, uint count)
{
	mShaders[mCurrentShader].Effect->GetVariableByName(name)->AsMatrix()->SetMatrixArray(data, 0, count);
}

void Renderer::setShaderFloat(const char * name, float data)
{
	mShaders[mCurrentShader].Effect->GetVariableByName(name)->AsScalar()->SetFloat(data);
}

void Renderer::setShaderFloatVector(const char * name, const float * data)
{
	mShaders[mCurrentShader].Effect->GetVariableByName(name)->AsVector()->SetFloatVector(data);
}

void Renderer::setShaderFloatVectorArray(const char * name, const float * data, uint count)
{
	mShaders[mCurrentShader].Effect->GetVariableByName(name)->AsVector()->SetFloatVectorArray(data, 0, count);
}

void Renderer::setShaderInt(const char * name, int data)
{
	mShaders[mCurrentShader].Effect->GetVariableByName(name)->AsScalar()->SetInt(data);
}

void Renderer::setShaderRawValue(const char * name, const void * data, uint sizeInBytes)
{
	mShaders[mCurrentShader].Effect->GetVariableByName(name)->SetRawValue(data, 0, sizeInBytes);
}

void Renderer::setVertexBuffer(VertexBufferID vb)
{
	//if(vb != mCurrentVertexBuffer)
	{
		mCurrentVertexBuffer = vb;
		uint stride = mVertexBuffers[vb].VertexSize;
		uint offset = 0;

		mContext->IASetVertexBuffers(0, 1, &mVertexBuffers[vb].Buffer, &stride, &offset);
	}
}

void Renderer::setIndexBuffer(IndexBufferID ib)
{
	//if(ib != mCurrentIndexBuffer)
	{
		mCurrentIndexBuffer = ib;
		DXGI_FORMAT format = mIndexBuffers[ib].IndexSize == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
		mContext->IASetIndexBuffer(mIndexBuffers[ib].Buffer, format, 0);
	}
}

void Renderer::setVertexFormat(VertexFormatID vf)
{
	mCurrentVertexFormat = vf;
	mContext->IASetInputLayout(mVertexFormats[vf].InputLayout);
}

void Renderer::setPrimitiveType(PrimitiveType type)
{
	if(type != mCurrentPrimType)
	{
		mCurrentPrimType = type;
		mContext->IASetPrimitiveTopology(primTopologies[type]);
	}
}

void Renderer::drawIndexed(uint num)
{
	mShaders[mCurrentShader].Pass->Apply(0, mContext);
	mContext->DrawIndexed(num, 0, 0);
}

void Renderer::drawIndexed()
{
	mShaders[mCurrentShader].Pass->Apply(0, mContext);
	mContext->DrawIndexed(mIndexBuffers[mCurrentIndexBuffer].NumIndices, 0, 0);
}

void Renderer::dispatch(uint threadGroupsX, uint threadGroupsY, uint threadGroupsZ)
{
	mShaders[mCurrentShader].Pass->Apply(0, mContext);
	mContext->Dispatch(threadGroupsX, threadGroupsY, threadGroupsZ);
}

void Renderer::drawFullScreenQuad()
{
	setPrimitiveType(TRIANGLE_LIST);
	setVertexFormat(VertexPosition::VertexFormat);
	setVertexBuffer(mFullScreenQuadVB);
	setIndexBuffer(mFullScreenQuadIB);
	drawIndexed();
}

void Renderer::applyConstantsWithoutDrawing()
{
	mShaders[mCurrentShader].Pass->Apply(0, mContext);
}

void Renderer::setRenderTargets(uint colorNum, TextureID * colorIds, TextureID depthStencilId)
{
	memset(mCurrentColorRTs, TEXTURE_NONE, sizeof(mCurrentColorRTs));

	ID3D11RenderTargetView* rtViews[MAX_COLOR_RTS];
	for(uint i = 0; i < colorNum; i++)
	{
		mCurrentColorRTs[i] = colorIds[i];
		rtViews[i] = mTextures[colorIds[i]].RTV;
	}
	mCurrentDepthRt = depthStencilId;

	mContext->OMSetRenderTargets(colorNum, rtViews, depthStencilId == TEXTURE_NONE ? NULL : mTextures[depthStencilId].DSV);
}

void Renderer::clear(bool clearColor, float * color, bool clearDepth, float depth, bool clearStencil, uint stencil)
{
	if(clearColor)
	{
		for(uint i = 0; i < MAX_COLOR_RTS; i++)
		{
			if(mCurrentColorRTs[i] != TEXTURE_NONE)
				mContext->ClearRenderTargetView(mTextures[mCurrentColorRTs[i]].RTV, color);
		}
	}

	if(clearDepth || clearStencil)
	{
		uint clearFlags = 0;
		if (clearDepth)   clearFlags |= D3D10_CLEAR_DEPTH;
		if (clearStencil) clearFlags |= D3D10_CLEAR_STENCIL;
		mContext->ClearDepthStencilView(mTextures[mCurrentDepthRt].DSV, clearFlags, depth, stencil);
	}
}

TextureID Renderer::addTexture(const char * filePath)
{
	ID3D11Resource * resource = 0;

	D3DX11_IMAGE_LOAD_INFO loadInfo;
	SecureZeroMemory( &loadInfo, sizeof(D3DX11_IMAGE_LOAD_INFO) );
	loadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	HR(D3DX11CreateTextureFromFile( mDevice, filePath, NULL, NULL, &resource, NULL ));

	D3D11_TEXTURE2D_DESC desc;
	((ID3D11Texture2D*)resource)->GetDesc( &desc );

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	srvDesc.Format = desc.Format;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MipLevels = desc.MipLevels;
	srvDesc.Texture2D.MostDetailedMip = 0;

	ID3D11ShaderResourceView * srv;
	HR(mDevice->CreateShaderResourceView(resource, &srvDesc, &srv));

	Texture tex;
	tex.Res = resource;
	tex.SRV = srv;
	tex.Width = desc.Width;
	tex.Height = desc.Height;
	tex.Format = desc.Format;

	mTextures.push_back(tex);
	return mTextures.size() - 1;
}

TextureID Renderer::addTexture2D(void * data, uint width, uint height, DXGI_FORMAT format, bool dynamic)
{
	Texture tex;
	tex.Width = width;
	tex.Height = height;
	tex.Format = format;

	D3D11_TEXTURE2D_DESC desc;
	desc.Width = width;
	desc.Height = height;
	desc.MipLevels = desc.ArraySize = 1;
	desc.Format = format;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_IMMUTABLE;
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
	desc.MiscFlags = 0;

	ID3D11Texture2D *texture = NULL;
	if(data == NULL)
	{
		assert(dynamic == true);
		HR(mDevice->CreateTexture2D( &desc, NULL, &texture ));
	}
	else
	{
		D3D11_SUBRESOURCE_DATA texData;
		texData.SysMemPitch = 0;
		texData.SysMemSlicePitch = 0;
		texData.pSysMem = data;
		HR(mDevice->CreateTexture2D( &desc, &texData, &texture ));
	}

	tex.Res = texture;

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	srvDesc.Format = desc.Format;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MipLevels = desc.MipLevels;
	srvDesc.Texture2D.MostDetailedMip = 0;

	HR(mDevice->CreateShaderResourceView(tex.Res, &srvDesc, &tex.SRV));
	
	mTextures.push_back(tex);
	return mTextures.size() - 1;
}

TextureID Renderer::addTexture3D(const TextureDesc & texDesc)
{
	assert(texDesc.Data != NULL || texDesc.Dynamic);
	assert( !(texDesc.CreateUAV && texDesc.Dynamic) );

	Texture tex;
	tex.Width = texDesc.Width;
	tex.Height = texDesc.Height;
	tex.Depth = texDesc.Depth;
	tex.Format = texDesc.Format;

	D3D11_TEXTURE3D_DESC desc;
	desc.Width = texDesc.Width;
	desc.Height = texDesc.Height;
	desc.Depth = texDesc.Depth;
	desc.MipLevels = 1;
	desc.Format = texDesc.Format;
	desc.Usage = texDesc.Dynamic ?
					D3D11_USAGE_DYNAMIC :
					texDesc.CreateUAV ?
						D3D11_USAGE_DEFAULT :
						D3D11_USAGE_IMMUTABLE;
	desc.BindFlags = 0;
	if(texDesc.CreateSRV) desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
	if(texDesc.CreateUAV) desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
	desc.CPUAccessFlags = texDesc.Dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
	desc.MiscFlags = 0;

	ID3D11Texture3D *texture = NULL;
	if(texDesc.Data == NULL)
	{
		assert(texDesc.Dynamic == true);
		HR(mDevice->CreateTexture3D( &desc, NULL, &texture ));
	}
	else
	{
		D3D11_SUBRESOURCE_DATA texData;
		texData.SysMemPitch = texDesc.DataPitch;
		texData.SysMemSlicePitch = texDesc.DataSlicePitch;
		texData.pSysMem = texDesc.Data;
		HR(mDevice->CreateTexture3D( &desc, &texData, &texture ));
	}

	tex.Res = texture;

	if(texDesc.CreateSRV)
	{
		D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
		srvDesc.Format = texDesc.SRVFormat;
		srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
		srvDesc.Texture3D.MipLevels = desc.MipLevels;
		srvDesc.Texture3D.MostDetailedMip = 0;
		HR(mDevice->CreateShaderResourceView(tex.Res, &srvDesc, &tex.SRV));
	}
	if(texDesc.CreateUAV)
	{
		D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
		uavDesc.Format = texDesc.UAVFormat;
		uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
		uavDesc.Texture3D.MipSlice = 0;
		uavDesc.Texture3D.FirstWSlice = 0;
		uavDesc.Texture3D.WSize = texDesc.Depth;
		HR(mDevice->CreateUnorderedAccessView(tex.Res, &uavDesc, &tex.UAV));
	}
	
	mTextures.push_back(tex);
	return mTextures.size() - 1;
}

TextureID Renderer::addTexture1D(const TextureDesc & texDesc)
{
	assert(texDesc.Data != NULL || texDesc.Dynamic);
	assert( !(texDesc.CreateUAV && texDesc.Dynamic) );

	Texture tex;
	tex.Width = texDesc.Width;
	tex.Format = texDesc.Format;

	D3D11_TEXTURE1D_DESC desc;
	desc.Width = texDesc.Width;
	desc.MipLevels = desc.ArraySize = 1;
	desc.Format = texDesc.Format;
	desc.Usage = texDesc.Dynamic ?
					D3D11_USAGE_DYNAMIC :
					texDesc.CreateUAV ?
						D3D11_USAGE_DEFAULT :
						D3D11_USAGE_IMMUTABLE;
	desc.BindFlags = 0;
	if(texDesc.CreateSRV) desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
	if(texDesc.CreateUAV) desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
	desc.CPUAccessFlags = texDesc.Dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
	desc.MiscFlags = 0;

	ID3D11Texture1D *texture = NULL;
	if(texDesc.Data == NULL)
	{
		assert(texDesc.Dynamic == true);
		HR(mDevice->CreateTexture1D( &desc, NULL, &texture ));
	}
	else
	{
		D3D11_SUBRESOURCE_DATA texData;
		texData.pSysMem = texDesc.Data;
		HR(mDevice->CreateTexture1D( &desc, &texData, &texture ));
	}

	tex.Res = texture;

	if(texDesc.CreateSRV)
	{
		D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
		srvDesc.Format = texDesc.SRVFormat;
		srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
		srvDesc.Texture1D.MipLevels = desc.MipLevels;
		srvDesc.Texture1D.MostDetailedMip = 0;
		HR(mDevice->CreateShaderResourceView(tex.Res, &srvDesc, &tex.SRV));
	}
	if(texDesc.CreateUAV)
	{
		D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
		uavDesc.Format = texDesc.UAVFormat;
		uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE1D;
		uavDesc.Texture1D.MipSlice = 0;
		HR(mDevice->CreateUnorderedAccessView(tex.Res, &uavDesc, &tex.UAV));
	}

	mTextures.push_back(tex);
	return mTextures.size() - 1;
}

byte * Renderer::mapTexture(TextureID tex, uint * rowPitch, uint * depthPitch)
{
	D3D11_MAPPED_SUBRESOURCE mapResource;
	HR(mContext->Map(mTextures[tex].Res, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapResource));

	if(rowPitch) *rowPitch = mapResource.RowPitch;
	if(depthPitch) *depthPitch = mapResource.DepthPitch;
	return (byte*)mapResource.pData;
}

void Renderer::unmapTexture(TextureID tex)
{
	mContext->Unmap(mTextures[tex].Res, 0);
}

void Renderer::resolveMSAATexture(TextureID src, TextureID dst)
{
	assert(mTextures[dst].Format == mTextures[src].Format);
	mContext->ResolveSubresource(mTextures[dst].Res, 0, mTextures[src].Res, 0, mTextures[src].Format);
}

TextureID Renderer::addRenderTarget(uint width, uint height, DXGI_FORMAT format, uint multisamples)
{
	Texture rt;
	rt.Width = width;
	rt.Height = height;
	rt.Format = format;

	D3D11_TEXTURE2D_DESC texDesc;
	texDesc.MiscFlags = 0;
	texDesc.Width = width;
	texDesc.Height = height;
	texDesc.MipLevels = 1;
	texDesc.ArraySize = 1;
	if(multisamples == 1)
	{
		texDesc.SampleDesc.Count = 1;
		texDesc.SampleDesc.Quality = 0;
	}
	else
	{
		texDesc.SampleDesc.Count = multisamples;
		texDesc.SampleDesc.Quality = 0;//D3D11_STANDARD_MULTISAMPLE_PATTERN;
	}
	texDesc.Format = format;
	texDesc.Usage = D3D11_USAGE_DEFAULT;
	texDesc.BindFlags =  D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	texDesc.CPUAccessFlags = 0;

	ID3D11Texture2D * tex2D;
	HR(mDevice->CreateTexture2D(&texDesc, NULL, &tex2D));
	rt.Res = tex2D;

	D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
	rtvDesc.Format = texDesc.Format;
	rtvDesc.ViewDimension = multisamples == 1 ? D3D11_RTV_DIMENSION_TEXTURE2D : D3D11_RTV_DIMENSION_TEXTURE2DMS;
	rtvDesc.Texture2D.MipSlice = 0;
	HR(mDevice->CreateRenderTargetView(rt.Res, &rtvDesc, &rt.RTV));

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	srvDesc.Format = texDesc.Format;
	srvDesc.ViewDimension = multisamples == 1 ? D3D11_SRV_DIMENSION_TEXTURE2D : D3D11_SRV_DIMENSION_TEXTURE2DMS;
	srvDesc.Texture2D.MipLevels = 1;
	srvDesc.Texture2D.MostDetailedMip = 0;
	HR(mDevice->CreateShaderResourceView(rt.Res, &srvDesc, &rt.SRV));

	mTextures.push_back(rt);
	return mTextures.size() - 1;
}

TextureID Renderer::addDepthTexture(uint width, uint height, DXGI_FORMAT format, uint multisamples)
{
	Texture rt;

	rt.Width = width;
	rt.Height = height;
	rt.Format = format;

	D3D11_TEXTURE2D_DESC texDesc;
	texDesc.Width = width;
	texDesc.Height = height;
	texDesc.MipLevels = 1;
	texDesc.ArraySize = 1;
	//texDesc.Format = DXGI_FORMAT_R32_TYPELESS;
	texDesc.Format = DXGI_FORMAT_R16_TYPELESS;
	texDesc.SampleDesc.Count = 1;
	texDesc.SampleDesc.Quality = 0;
	texDesc.Usage = D3D11_USAGE_DEFAULT;
	texDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
	texDesc.CPUAccessFlags = 0;
	texDesc.MiscFlags = 0;

	D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
	dsvDesc.Flags = 0;
	//dsvDesc.Format = DXGI_FORMAT_D32_FLOAT;
	dsvDesc.Format = DXGI_FORMAT_D16_UNORM;
	dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
	dsvDesc.Texture2D.MipSlice = 0;

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	//srvDesc.Format = DXGI_FORMAT_R32_FLOAT;
	srvDesc.Format = DXGI_FORMAT_R16_UNORM;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MipLevels = texDesc.MipLevels;
	srvDesc.Texture2D.MostDetailedMip = 0;

	ID3D11Texture2D * tex2D;
	HR(mDevice->CreateTexture2D(&texDesc, NULL, &tex2D));
	rt.Res = tex2D;
	HR(mDevice->CreateDepthStencilView(rt.Res, &dsvDesc, &rt.DSV));
	HR(mDevice->CreateShaderResourceView(rt.Res, &srvDesc, &rt.SRV));

	mTextures.push_back(rt);
	return mTextures.size() - 1;
}

ShaderID Renderer::addShader(const char * filePath, ShaderMacro * macros, uint numMacros)
{
	DWORD shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
//#if defined( DEBUG ) || defined( _DEBUG )
//	shaderFlags |= D3D10_SHADER_DEBUG;
//	shaderFlags |= D3D10_SHADER_SKIP_OPTIMIZATION;
//#else
	shaderFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL3;
//#endif

	D3D10_SHADER_MACRO * d3dMacros = NULL;
	if(numMacros > 0)
	{
		d3dMacros = new D3D10_SHADER_MACRO[numMacros + 1];
		for(uint i = 0; i < numMacros; i++)
		{
			d3dMacros[i].Name = macros[i].Name;
			d3dMacros[i].Definition = macros[i].Definition;
		}
		d3dMacros[numMacros].Name = NULL;
		d3dMacros[numMacros].Definition = NULL;
	}

	ID3D10Blob *dataBlob = 0, *errorBlob = 0;
	HRESULT hr = D3DX11CompileFromFile(filePath, d3dMacros, NULL, NULL, "fx_5_0", shaderFlags, 0, NULL, &dataBlob, &errorBlob, NULL);
	if (FAILED(hr))
	{
		if( errorBlob )
		{
			MessageBoxA(0, (char*)errorBlob->GetBufferPointer(), 0, 0);
			ReleaseCOM(errorBlob);
		}
		DXTrace(__FILE__, (DWORD)__LINE__, hr, filePath, true);
		return NULL;
	}
	delete d3dMacros;

	ID3DX11Effect * effect = NULL;
	HR(D3DX11CreateEffectFromMemory(dataBlob->GetBufferPointer(), dataBlob->GetBufferSize(), 0, mDevice, &effect));

	Shader shader = { effect, effect->GetTechniqueByIndex(0)->GetPassByIndex(0) };
	mShaders.push_back(shader);
	return mShaders.size() - 1;
}

ShaderID Renderer::addShaderFromBinaryFile(const char * filePath)
{
	FILE * file = fopen(filePath, "rb");
	fseek(file, 0, SEEK_END);
	long length = ftell(file);
	fseek(file, 0, SEEK_SET);
	char * buffer = new char[length];
	fread(buffer, length, 1, file);
	fclose(file);

	ID3DX11Effect * effect = NULL;
	HR(D3DX11CreateEffectFromMemory(buffer, length, 0, mDevice, &effect));

	delete[] buffer;

	Shader shader = { effect, effect->GetTechniqueByIndex(0)->GetPassByIndex(0) };
	mShaders.push_back(shader);
	return mShaders.size() - 1;
}

VertexBufferID Renderer::addVertexBuffer(void * data, uint numVerts, size_t vertSize, bool dynamic)
{
	VertexBuffer vb;
	vb.NumVertices = numVerts;
	vb.VertexSize = vertSize;

	D3D11_BUFFER_DESC bufDesc;
	bufDesc.Usage = dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_IMMUTABLE;
	bufDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bufDesc.CPUAccessFlags = dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
	bufDesc.MiscFlags = 0;
	bufDesc.ByteWidth = vertSize * numVerts;

	if(data == NULL)
	{
		assert(dynamic);
		HR(mDevice->CreateBuffer(&bufDesc, NULL, &vb.Buffer));
	}
	else
	{
		D3D11_SUBRESOURCE_DATA bufData;
		bufData.SysMemPitch = 0;
		bufData.SysMemSlicePitch = 0;
		bufData.pSysMem = data;
		HR(mDevice->CreateBuffer(&bufDesc, &bufData, &vb.Buffer));
	}
	
	mVertexBuffers.push_back(vb);
	return mVertexBuffers.size() - 1;
}

byte * Renderer::mapVertexBuffer(VertexBufferID vb)
{
	D3D11_MAPPED_SUBRESOURCE mapResource;
	HR(mContext->Map(mVertexBuffers[vb].Buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapResource));

	return (byte*)mapResource.pData;
}

void Renderer::unmapVertexBuffer(VertexBufferID vb)
{
	mContext->Unmap(mVertexBuffers[vb].Buffer, 0);
}

IndexBufferID Renderer::addIndexBuffer(void * data, uint numIndices, size_t indexSize, bool dynamic)
{
	IndexBuffer ib;
	ib.NumIndices = numIndices;
	ib.IndexSize = indexSize;

	D3D11_BUFFER_DESC bufDesc;
	bufDesc.Usage = dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_IMMUTABLE;
	bufDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bufDesc.CPUAccessFlags = dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
	bufDesc.MiscFlags = 0;
	bufDesc.ByteWidth = indexSize * numIndices;

	D3D11_SUBRESOURCE_DATA bufData;
	bufData.SysMemPitch = 0;
	bufData.SysMemSlicePitch = 0;
	bufData.pSysMem = data;

	HR(mDevice->CreateBuffer(&bufDesc, &bufData, &ib.Buffer));

	mIndexBuffers.push_back(ib);
	return mIndexBuffers.size() - 1;
}

VertexFormatID Renderer::addVertexFormat(const VFAttribute * attribs, uint numAttribs, ShaderID shader)
{
	static const DXGI_FORMAT formats[][4] = {
		DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32B32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT,
		DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_UNKNOWN,         DXGI_FORMAT_R16G16B16A16_FLOAT,
		DXGI_FORMAT_R8_UNORM,  DXGI_FORMAT_R8G8_UNORM,   DXGI_FORMAT_UNKNOWN,         DXGI_FORMAT_R8G8B8A8_UNORM
	};

	static const char *semantics[] = {
		"POSITION",
		"TEXCOORD",
		"NORMAL",
		"TANGENT",
		"BINORMAL"
	};

	D3D11_INPUT_ELEMENT_DESC layoutDesc[MAX_VERTEX_STREAMS];
	uint currentOffset = 0;
	for(uint i = 0; i < numAttribs; i++)
	{
		D3D11_INPUT_ELEMENT_DESC elemDesc =
			{ semantics[attribs[i].Type], attribs[i].Stream,
			formats[attribs[i].Format][attribs[i].Size - 1],
			0, currentOffset, D3D11_INPUT_PER_VERTEX_DATA, 0 };

		layoutDesc[i] = elemDesc;

		static const size_t formatUnitSize[] = { 4, 2, 1 };
		currentOffset += attribs[i].Size * formatUnitSize[attribs[i].Format];
	}

	D3DX11_PASS_SHADER_DESC vsPassDesc;
	D3DX11_EFFECT_SHADER_DESC vsDesc;
	mShaders[shader].Pass->GetVertexShaderDesc(&vsPassDesc);
	vsPassDesc.pShaderVariable->GetShaderDesc(vsPassDesc.ShaderIndex, &vsDesc);

	VertexFormat vf;
	HR(mDevice->CreateInputLayout( layoutDesc, numAttribs, vsDesc.pBytecode, vsDesc.BytecodeLength, &vf.InputLayout ) );
	
	mVertexFormats.push_back(vf);
	return mVertexFormats.size() - 1;
}

void Renderer::setViewportFromTextureDim( TextureID tex )
{
	D3D11_VIEWPORT vp;
	vp.TopLeftX = 0.0f;
	vp.TopLeftY = 0.0f;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.Width = (float)mTextures[tex].Width;
	vp.Height = (float)mTextures[tex].Height;

	mContext->RSSetViewports(1, &vp);
}

Viewport Renderer::getViewport()
{
	uint numVp = 1;
	D3D11_VIEWPORT vp;
	mContext->RSGetViewports(&numVp, &vp);
	return copyViewport(vp);
}

void Renderer::setViewport(const Viewport & vp)
{
	D3D11_VIEWPORT d3dVp = copyViewport(vp);
	mContext->RSSetViewports(1, &d3dVp);
}

TextureID Renderer::getDefaultFramebuffer()
{
	return mDefaultFramebuffer;
}

TextureID Renderer::getDefaultDepthStencilBuffer()
{
	return mDefaultDepthStencil;
}

void Renderer::createFullScreenQuadBuffers()
{
	VertexPosition vertices[4];
	vertices[0].Position = XMFLOAT3(-1.0f, -1.0f, 0.5f);
	vertices[1].Position = XMFLOAT3(-1.0f,  1.0f, 0.5f);
	vertices[2].Position = XMFLOAT3( 1.0f, -1.0f, 0.5f);
	vertices[3].Position = XMFLOAT3( 1.0f,  1.0f, 0.5f);
	mFullScreenQuadVB = addVertexBuffer(&vertices[0], 4, sizeof(VertexPosition));

	short indices[6];
	indices[0] = 0;
	indices[1] = 1;
	indices[2] = 2;

	indices[3] = 1;
	indices[4] = 3;
	indices[5] = 2;
	mFullScreenQuadIB = addIndexBuffer(indices, 6, sizeof(short));
}

//DepthMap mgfx::createDepthMap(ID3D11Device * device, uint width, uint height, uint arraySize)
//{
//	DepthMap map;
//
//	D3D11_TEXTURE2D_DESC texDesc;
//	texDesc.Width = width;
//	texDesc.Height = height;
//	texDesc.MipLevels = 1;
//	texDesc.ArraySize = arraySize;
//	//texDesc.Format = DXGI_FORMAT_R32_TYPELESS;
//	texDesc.Format = DXGI_FORMAT_R16_TYPELESS;
//	texDesc.SampleDesc.Count = 1;
//	texDesc.SampleDesc.Quality = 0;
//	texDesc.Usage = D3D11_USAGE_DEFAULT;
//	texDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
//	texDesc.CPUAccessFlags = 0;
//	texDesc.MiscFlags = 0;
//
//	D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
//	dsvDesc.Flags = 0;
//	//dsvDesc.Format = DXGI_FORMAT_D32_FLOAT;
//	dsvDesc.Format = DXGI_FORMAT_D16_UNORM;
//	dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
//	dsvDesc.Texture2DArray.ArraySize = arraySize;
//	dsvDesc.Texture2DArray.MipSlice = 0;
//	dsvDesc.Texture2DArray.FirstArraySlice = 0;
//
//	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
//	//srvDesc.Format = DXGI_FORMAT_R32_FLOAT;
//	srvDesc.Format = DXGI_FORMAT_R16_UNORM;
//	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
//	srvDesc.Texture2DArray.ArraySize = arraySize;
//	srvDesc.Texture2DArray.FirstArraySlice = 0;
//	srvDesc.Texture2DArray.MipLevels = texDesc.MipLevels;
//	srvDesc.Texture2DArray.MostDetailedMip = 0;
//
//	HR(device->CreateTexture2D(&texDesc, NULL, &map.Texture));
//	HR(device->CreateDepthStencilView(map.Texture, &dsvDesc, &map.DSView));
//	HR(device->CreateShaderResourceView(map.Texture, &srvDesc, &map.SRView));
//
//	map.Width = width;
//	map.Height = height;
//
//	return map;
//}
//
//void mgfx::releaseDepthMap(DepthMap & map)
//{
//	ReleaseCOM(map.SRView);
//	ReleaseCOM(map.DSView);
//	ReleaseCOM(map.Texture);
//}
//
//RenderTarget mgfx::createRenderTarget(ID3D11Device * device, uint width, uint height, DXGI_FORMAT format, uint multisamples)
//{
//	RenderTarget rt;
//
//	rt.Width = width;
//	rt.Height = height;
//
//	D3D11_TEXTURE2D_DESC texDesc;
//	texDesc.MiscFlags = 0;
//	texDesc.Width = width;
//	texDesc.Height = height;
//	texDesc.MipLevels = 1;
//	texDesc.ArraySize = 1;
//	if(multisamples == 1)
//	{
//		texDesc.SampleDesc.Count = 1;
//		texDesc.SampleDesc.Quality = 0;
//	}
//	else
//	{
//		texDesc.SampleDesc.Count = multisamples;
//		texDesc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;
//	}
//	texDesc.Format = format;
//	texDesc.Usage = D3D11_USAGE_DEFAULT;
//	texDesc.BindFlags =  D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
//	texDesc.CPUAccessFlags = 0;
//	HR(device->CreateTexture2D(&texDesc, NULL, &rt.Texture));
//
//	D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
//	rtvDesc.Format = texDesc.Format;
//	rtvDesc.ViewDimension = multisamples == 1 ? D3D11_RTV_DIMENSION_TEXTURE2D : D3D11_RTV_DIMENSION_TEXTURE2DMS;
//	rtvDesc.Texture2D.MipSlice = 0;
//	HR(device->CreateRenderTargetView(rt.Texture, &rtvDesc, &rt.RTView));
//
//	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
//	srvDesc.Format = texDesc.Format;
//	srvDesc.ViewDimension = multisamples == 1 ? D3D11_SRV_DIMENSION_TEXTURE2D : D3D11_SRV_DIMENSION_TEXTURE2DMS;
//	srvDesc.Texture2D.MipLevels = 1;
//	srvDesc.Texture2D.MostDetailedMip = 0;
//	HR(device->CreateShaderResourceView(rt.Texture, &srvDesc, &rt.SRView));
//
//	return rt;
//}